// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CloudDriveManagerExtensions.cs" company="AzureContrib (Open source library http://azurecontrib.codeplex.com)">
//   Microsoft Public License (Ms-PL)
// </copyright>
// <summary>
//   Extensions on ICloudDriveManager
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;

using AzureContrib.WindowsAzure.Diagnostics;
using AzureContrib.WindowsAzure.StorageClient.Properties;

using Microsoft.WindowsAzure;

namespace AzureContrib.WindowsAzure.StorageClient
{
	/// <summary>
	/// Extensions on ICloudDriveManager
	/// </summary>
	public static class CloudDriveManagerExtensions
	{
		/// <summary>
		/// Create a cloud drive in blob storage that does not exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <returns>True if the specific Cloud Drive (Page Blob) exists.</returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."), 
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static bool Exists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(cloudStorageAccount, "cloudStorageAccount");

			var driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			return cloudDriveManager.Exists(cloudStorageAccount.Credentials.Wrap(), driveAddress);
		}

		/// <summary>
		/// Create a cloud drive in blob storage that does not exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <param name="driveAddress">The address of the Page Blob created.</param>
		/// <returns>True if the specific Cloud Drive (Page Blob) exists.</returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."), 
		 SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "4#", Justification = "This method checks for existance and creates if not exists. It is the right design for an extension method."),
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static bool Exists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int? sizeInMegabytes, out Uri driveAddress)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(cloudStorageAccount, "cloudStorageAccount");

			driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			var exists = cloudDriveManager.Exists(cloudStorageAccount, driveRelativePath);

			if (!exists && sizeInMegabytes.HasValue)
			{
				cloudDriveManager.Create(cloudStorageAccount.Credentials.Wrap(), driveAddress, sizeInMegabytes.Value);
			}

			return exists;
		}

		/// <summary>
		/// Create a cloud drive in blob storage that does not exist.
		/// </summary>
		/// <param name="cloudDriveManager">
		/// The CloudDrive Manager to extend.
		/// </param>
		/// <param name="cloudStorageAccount">
		/// The Windows Azure Storage account to access.
		/// </param>
		/// <param name="driveRelativePath">
		/// The Page Blob Cloud Dive.
		/// </param>
		/// <param name="sizeInMegabytes">
		/// If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.
		/// </param>
		/// <returns>
		/// The create.
		/// </returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."), 
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static Uri Create(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int sizeInMegabytes)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(cloudStorageAccount, "cloudStorageAccount");

			var driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			cloudDriveManager.Create(cloudStorageAccount.Credentials.Wrap(), driveAddress, sizeInMegabytes);
			return driveAddress;
		}

		/// <summary>
		/// Creates the CloudDrive if it does not already exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <returns>True if the CloudDrive did not already exist and was created; otherwise, false.</returns>
		public static bool CreateIfNotExists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int sizeInMegabytes)
		{
			Uri uri;
			return CreateIfNotExists(cloudDriveManager, cloudStorageAccount, driveRelativePath, sizeInMegabytes, out uri);
		}

		/// <summary>
		/// Creates the CloudDrive if it does not already exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <param name="driveAddress">The CloudDrive blob VHD dive address.</param>
		/// <returns>True if the CloudDrive did not already exist and was created; otherwise, false.</returns>
		[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "4#", Justification = "This method checks for existance and creates if not exists. It is the right design for an extension method."), 
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static bool CreateIfNotExists(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int sizeInMegabytes, out Uri driveAddress)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(cloudStorageAccount, "cloudStorageAccount");

			var exists = cloudDriveManager.Exists(cloudStorageAccount, driveRelativePath);
			if (exists)
			{
				driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			}
			else
			{
				driveAddress = cloudDriveManager.Create(cloudStorageAccount, driveRelativePath, sizeInMegabytes);
			}

			return !exists;
		}

		/// <summary>
		/// Creates the CloudDrive if it does not already exist.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="storageCredentials">The Windows Azure Storage Credentials to access Cloud Storage.</param>
		/// <param name="driveAddress">The CloudDrive blob VHD dive address.</param>
		/// <param name="sizeInMegabytes">If the drive to mount does not exist then create it with the given size. No value means do not create if it does not exist.</param>
		/// <returns>True if the CloudDrive did not already exist and was created; otherwise, false.</returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."), 
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static bool CreateIfNotExists(this ICloudDriveManager cloudDriveManager, StorageCredentials storageCredentials, Uri driveAddress, int sizeInMegabytes)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(storageCredentials, "storageCredentials");

			if (cloudDriveManager.Exists(storageCredentials.Wrap(), driveAddress))
			{
				return false;
			}

			cloudDriveManager.Create(storageCredentials.Wrap(), driveAddress, sizeInMegabytes);
			return true;
		}

		/// <summary>
		/// Mount a Windows Azure Drive and return the DirectoryInfo for it.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <returns>A rooted path to the drive mounted.</returns>
		public static DirectoryInfo Mount(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath)
		{
			return Mount(cloudDriveManager, cloudStorageAccount, driveRelativePath, 0, DriveMountOptions.Force);
		}

		/// <summary>
		/// Mount a Windows Azure Drive and return the DirectoryInfo for it.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="cacheSize">The part of the cache to take up for this drive.</param>
		/// <returns>A rooted path to the drive mounted.</returns>
		public static DirectoryInfo Mount(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int cacheSize)
		{
			return Mount(cloudDriveManager, cloudStorageAccount, driveRelativePath, cacheSize, DriveMountOptions.Force);
		}

		/// <summary>
		/// Mount a Windows Azure Drive and return the DirectoryInfo for it.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="driveMountOptions">Drive mounting options.</param>
		/// <returns>A rooted path to the drive mounted.</returns>
		public static DirectoryInfo Mount(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, DriveMountOptions driveMountOptions)
		{
			return Mount(cloudDriveManager, cloudStorageAccount, driveRelativePath, 0, driveMountOptions);
		}

		/// <summary>
		/// Mount a Windows Azure Drive and return the DirectoryInfo for it.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="cloudStorageAccount">The Windows Azure Storage account to access.</param>
		/// <param name="driveRelativePath">The Page Blob Cloud Dive.</param>
		/// <param name="cacheSize">The part of the cache to take up for this drive.</param>
		/// <param name="driveMountOptions">Drive mounting options.</param>
		/// <returns>A rooted path to the drive mounted.</returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."), 
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static DirectoryInfo Mount(this ICloudDriveManager cloudDriveManager, CloudStorageAccount cloudStorageAccount, string driveRelativePath, int cacheSize, DriveMountOptions driveMountOptions)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(cloudStorageAccount, "cloudStorageAccount");

			var driveAddress = cloudStorageAccount.BlobEndpoint.Append(driveRelativePath);
			return cloudDriveManager.Mount(cloudStorageAccount.Credentials.Wrap(), driveAddress, cacheSize, driveMountOptions);
		}

		/// <summary>
		/// Dismount all drives mounted on this application.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="trace">The current tracer.</param>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."),
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static void DismountAllDrives(this ICloudDriveManager cloudDriveManager, ITrace trace)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(trace, "trace");

			trace.Information(Strings.ManagedDrivesDismounting);

			while (Microsoft.WindowsAzure.StorageClient.CloudDrive.GetMountedDrives().Any())
			{
				cloudDriveManager.Dismount(new DirectoryInfo(Microsoft.WindowsAzure.StorageClient.CloudDrive.GetMountedDrives().First().Key));
			}

			trace.Information(Strings.ManagedDrivesDismounted);
		}

		/// <summary>
		/// Dismount and delete from blob storage all drives mounted on this application.
		/// </summary>
		/// <param name="cloudDriveManager">The CloudDrive Manager to extend.</param>
		/// <param name="trace">The current tracer.</param>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking."),
		 SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static void DeleteAllDrives(this ICloudDriveManager cloudDriveManager, ITrace trace)
		{
			Assumes.NotNull(cloudDriveManager, "cloudDriveManager");
			Assumes.NotNull(trace, "trace");

			trace.Information(Strings.ManagedDrivesDeleting);

			while (Microsoft.WindowsAzure.StorageClient.CloudDrive.GetMountedDrives().Any())
			{
				cloudDriveManager.Delete(new DirectoryInfo(Microsoft.WindowsAzure.StorageClient.CloudDrive.GetMountedDrives().First().Key));
			}

			trace.Information(Strings.ManagedDrivesDeleted);
		}
	}
}